﻿/* ----------------------------------------------------------------------------
 * Copyright (c) Philip Stears 
 *
 * This source code is subject to terms and conditions of the Microsoft Public 
 * License (Ms-PL).
 * 
 * By using this source code in any fashion, you are agreeing to be bound 
 * by the terms of the Microsoft Public License.
 * 
 * To request a copy of the Microsoft Public License, send a request by email
 * to license@connectbasic.net.
 *
 * This notice must not be removed from this software.
 * ---------------------------------------------------------------------------- */

namespace ConnectBasic.ObjectModel
{
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.Linq;
    using ConnectBasic.Execution;
    using ConnectBasic.Execution.Binding;
    using Microsoft.Scripting;
    using DlrExpression = Microsoft.Linq.Expressions.Expression;

    /// <summary>
    /// Represents a method call.
    /// </summary>
    public class CallExpression : ExpressionBase
    {
        private string mTargetName;
        private ReadOnlyCollection<ExpressionBase> mArguments;

        /// <summary>
        /// Initializes a new instance of the <see cref="CallExpression" /> method.
        /// </summary>
        /// <param name="targetName">The name of the method to call.</param>
        /// <param name="arguments">The arguments to the method.</param>
        /// <param name="expressionSpan">
        /// The span which describes the span in the source text from which the 
        /// expression was derived.
        /// </param>
        public CallExpression(SourceSpan expressionSpan, string targetName, IEnumerable<ExpressionBase> arguments)
            : base(expressionSpan)
        {
            mTargetName = targetName;
            mArguments = new ReadOnlyCollection<ExpressionBase>(arguments as IList<ExpressionBase> ?? new List<ExpressionBase>(arguments));
        }

        /// <summary>
        /// Gets the name of the method to be called.
        /// </summary>
        public string TargetName
        {
            get { return mTargetName; }
        }

        /// <summary>
        /// Gets the arguments to be provided to the method.
        /// </summary>
        public IEnumerable<ExpressionBase> Arguments
        {
            get { return mArguments; }
        }

        /// <inheritdoc />
        public override string GetDebuggerDisplay()
        {
            string[] argumentDebuggerDisplay = new string[mArguments.Count];

            for (int i = 0; i < mArguments.Count; i++)
            {
                argumentDebuggerDisplay[i] = mArguments[i].GetDebuggerDisplay();
            }

            return mTargetName + "(" + string.Join(", ", argumentDebuggerDisplay) + ")";
        }

        /// <inheritdoc />
        public override DlrExpression Compile(BasicCodeGenerator gen)
        {

            // Is there a variable in scope with the name of the "method" ?
            // if so - we're looking at index access not a method call
            DlrExpression result = gen.Scope.MakeGetValueExpression(mTargetName, gen.MeParameter);

            if (result != null)
            {

                // Convert arguments
                var actualArguments = (from arg in mArguments select arg.Compile(gen)).ToArray();
                var passArguments = new DlrExpression[1 + actualArguments.Length];
                actualArguments.CopyTo(passArguments, 1);

                passArguments[0] = result;

                // Emit a dynamic call
                return DlrExpression.Dynamic(new BasicGetIndexBinder(new CallInfo(passArguments.Length)), typeof(object), passArguments);

            }
            else
            {

                // Convert arguments
                var actualArguments = (from arg in mArguments select arg.Compile(gen)).ToArray();
                var passArguments = new DlrExpression[2 + actualArguments.Length];
                actualArguments.CopyTo(passArguments, 2);

                passArguments[0] = DlrExpression.Constant(mTargetName);
                passArguments[1] = gen.MeParameter;

                // Emit a dynamic call
                return DlrExpression.Dynamic(new BasicCallBinder(new CallInfo(passArguments.Length)), typeof(object), passArguments);
            }
        }
    }
}
